package main

import (
	"context"
	"fmt"
	"io"
	"log/slog"
	"os"
	"os/signal"
	"quipus/internal/profile"
	"quipus/internal/utils"
	"strings"
	"syscall"

	"quipus/internal/server"

	"quipus/internal/server/version"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const (
	greetingBanner = `
	#######  #     #  ###  #######  #     #  #######  
	#     #  #     #   #   #     #  #     #  #     # 
	#     #  #     #   #   #     #  #     #  #       
	#     #  #     #   #   #######  #     #   #####  
	#   # #  #     #   #   #        #     #        # 
	#    #   #     #   #   #        #     #  #     # 
	 #### #   #####   ###  #        #######  #######  
`
)

var (
	rootCmd = &cobra.Command{
		Use:   "quipus",
		Short: `An open source, lightweight note-taking service. Easily capture and share your great thoughts.`,
		Run: func(_ *cobra.Command, _ []string) {
			initConfig()
			instanceProfile := &profile.Profile{
				Mode:         viper.GetString("mode"),
				Addr:         viper.GetString("host"),
				Port:         viper.GetInt("port"),
				Data:         viper.GetString("data"),
				Driver:       viper.GetString("driver"),
				DSN:          viper.GetString("dsn"),
				Public:       viper.GetBool("public"),
				PasswordAuth: viper.GetBool("password-auth"),
				InstanceURL:  viper.GetString("instance-url"),
				CertFile:     viper.GetString("cert-path"),
				KeyFile:      viper.GetString("key-path"),
				// TODO: add more config options
				Version: version.GetCurrentVersion(viper.GetString("mode")),
			}
			if err := instanceProfile.Validate(); err != nil {
				panic(err)
			}
			skey := viper.GetString("encryption-key")
			if skey == "" {
				fmt.Print("key is empty")
				os.Exit(-111)
			}

			if instanceProfile.DSN != "" {
				key := []byte(skey)
				dsn, err := utils.Decrypt(instanceProfile.DSN, key)
				if err == nil {
					instanceProfile.DSN = dsn
				}
			}

			ctx, cancel := context.WithCancel(context.Background())

			// s, err := server.NewServer(ctx, instanceProfile)
			s, err := server.NewServer(ctx, instanceProfile)
			// s, err := server.NewServerEcho(ctx, instanceProfile)

			if err != nil {
				cancel()
				slog.Error("failed to create server", "error", err)
				return
			}

			c := make(chan os.Signal, 1)
			// Trigger graceful shutdown on SIGINT or SIGTERM.
			// The default signal sent by the `kill` command is SIGTERM,
			// which is taken as the graceful shutdown signal for many systems, eg., Kubernetes, Gunicorn.
			signal.Notify(c, os.Interrupt, syscall.SIGTERM)
			// s.RegisterFiberRoutes(ctx)
			s.Start(ctx)

			printGreetings(instanceProfile)

			go func() {
				<-c
				s.Shutdown(ctx)
				cancel()
			}()
			// Wait for CTRL-C.
			<-ctx.Done()
		},
	}
)

func init() {
	viper.SetDefault("mode", "demo")
	viper.SetDefault("driver", "postgres")
	viper.SetDefault("port", 7227)
	viper.SetDefault("password-auth", true)
	viper.SetDefault("encryption-key", "qpworld1234567")

	rootCmd.PersistentFlags().String("mode", "demo", `mode of server, can be "prod" or "dev" or "demo" or "set"`)
	rootCmd.PersistentFlags().String("host", "", "address of server")
	rootCmd.PersistentFlags().Int("port", 8081, "port of server")
	rootCmd.PersistentFlags().String("data", "", "data directory")
	rootCmd.PersistentFlags().String("driver", "postgres", "database driver")
	rootCmd.PersistentFlags().String("dsn", "", "database source name(aka. DSN)")
	rootCmd.PersistentFlags().Bool("public", false, "")
	rootCmd.PersistentFlags().Bool("password-auth", true, "")
	rootCmd.PersistentFlags().String("instance-url", "", "the url of your qp instance")
	rootCmd.PersistentFlags().String("encryption-key", "", "key for DSN encryption/decryption")
	rootCmd.PersistentFlags().String("cert-path", "", "path to the certificate file")
	rootCmd.PersistentFlags().String("key-path", "", "path to the key file")
	rootCmd.PersistentFlags().String("san", "", "san of the certificate for self-signed certificate")

	if err := viper.BindPFlag("mode", rootCmd.PersistentFlags().Lookup("mode")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("host", rootCmd.PersistentFlags().Lookup("host")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("data", rootCmd.PersistentFlags().Lookup("data")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("driver", rootCmd.PersistentFlags().Lookup("driver")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("dsn", rootCmd.PersistentFlags().Lookup("dsn")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("public", rootCmd.PersistentFlags().Lookup("public")); err != nil {
		panic(err)
	}
	if err := viper.BindPFlag("password-auth", rootCmd.PersistentFlags().Lookup("password-auth")); err != nil {
		panic(err)
	}

	if err := viper.BindPFlag("key-path", rootCmd.PersistentFlags().Lookup("key-path")); err != nil {
		panic(err)
	}

	if err := viper.BindPFlag("cert-path", rootCmd.PersistentFlags().Lookup("cert-path")); err != nil {
		panic(err)
	}

	if err := viper.BindPFlag("san", rootCmd.PersistentFlags().Lookup("san")); err != nil {
		panic(err)
	}

	if err := viper.BindPFlag("instance-url", rootCmd.PersistentFlags().Lookup("instance-url")); err != nil {
		panic(err)
	}

	viper.SetEnvPrefix("qp")
	viper.AutomaticEnv()
	if err := viper.BindEnv("password-auth", "QP_PASSWORD_AUTH"); err != nil {
		panic(err)
	}
	if err := viper.BindEnv("instance-url", "QP_INSTANCE_URL"); err != nil {
		panic(err)
	}
}

func initConfig() {
	mode := viper.GetString("mode")
	// 如果是 set 模式，对 DSN 进行加密
	if mode == "encrypt" {
		key := []byte(viper.GetString("encryption-key"))
		dsn := viper.GetString("dsn")
		if dsn == "" {
			fmt.Print("dsn is empty, pls input dsn")
			os.Exit(0)
		}

		encrypted, err := utils.Encrypt(dsn, key)
		if err != nil {
			slog.Error("failed to encrypt DSN", "error", err)
			os.Exit(1)
		}
		fmt.Printf("%s", encrypted)
		os.Exit(0)
	}

	if mode == "decrypt" {
		key := []byte(viper.GetString("encryption-key"))
		dsn := viper.GetString("dsn")
		if dsn == "" {
			fmt.Print("dsn is empty, pls input dsn")
			os.Exit(0)
		}

		encrypted, err := utils.Decrypt(dsn, key)
		if err != nil {
			slog.Error("failed to decrypt DSN", "error", err)
			os.Exit(1)
		}
		fmt.Printf("%s", encrypted)
		os.Exit(0)
	}
}

func printGreetings(profile *profile.Profile) {
	fmt.Printf(`---
Server profile
version: %s
data: %s
dsn: %s
addr: %s
port: %d
mode: %s
public: %t
password-auth: %t
driver: %s
---
`, profile.Version, profile.Data, profile.DSN, profile.Addr, profile.Port, profile.Mode, profile.Public, profile.PasswordAuth, profile.Driver)

	print(greetingBanner)
	if len(profile.Addr) == 0 {
		fmt.Printf("Version %s has been started on port %d\n", profile.Version, profile.Port)
	} else {
		fmt.Printf("Version %s has been started on address '%s' and port %d\n", profile.Version, profile.Addr, profile.Port)
	}
	fmt.Printf(`---
See more in:
👉GitHub: %s
---
`, "https://gitee.com/quipus/quipus")
}

func main() {
	logpath := os.Getenv("QPLOG_PATH")
	loglevel := os.Getenv("QPLOG_LEVEL")
	wr := []io.Writer{os.Stdout}
	if logpath != "" {
		logFile, err := os.OpenFile(logpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			panic(err)
		}
		wr = append(wr, logFile)

	}

	// 创建多输出writer
	multiWriter := io.MultiWriter(wr...)

	level := slog.LevelInfo
	if loglevel != "" {
		loglevel = strings.ToUpper(loglevel)
	}
	switch loglevel {
	case "DEBUG":
		level = slog.LevelDebug
	case "INFO":
		level = slog.LevelInfo
	case "WARN":
		level = slog.LevelWarn
	case "ERROR":
		level = slog.LevelError
	default:
		level = slog.LevelInfo
	}

	// 创建新的 logger
	logger := slog.New(slog.NewJSONHandler(multiWriter, &slog.HandlerOptions{
		Level: level,
	}))

	slog.SetDefault(logger)

	if err := rootCmd.Execute(); err != nil {
		panic(err)
	}
}
